/*
 * Created on Jan 31, 2008
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
package it.unibo.deis.lia.mmhc.rom;

import it.unibo.deis.lia.networkInterface.*; //import it.unibo.deis.lia.networkInterface.Bluetooth.ConnectionObserver;
import it.unibo.deis.lia.mmhc.com.ConnectorManager;
import it.unibo.deis.lia.mmhc.nip.*;
import it.unibo.deis.lia.mmhc.util.*;

import java.util.*;

//import java.net.*;
//import java.io.*;

public class RoutingManager extends Thread { // implements ConnectionObserver{
	/*
	 * sempre in ascolto su ogni interfaccia utilizzata per fornire connettività
	 * a) quali client? b) quali requisiti per ciascun client?
	 * 
	 * 1) recuperare i channel attivi da CoM 2) richiedere ai connector di
	 * ciascun channel informazioni sul path (pull) 3) mettersi in ascolto per
	 * eventuali update dai connector (push)
	 * 
	 * 
	 * ogni volta che: c'e' un nuovo channel un vecchio channel scompare ci sono
	 * nuove informazioni da uno dei connector a) valutare se cambiare le regole
	 * di routing b) notificare il cambiamento ai vari client
	 */
	private ConnectorManager connectorManager;
	private NetworkInterfaceProvider nip;

	protected double localRequiredReliability = 1.0;

	private Vector<Channel> availableChannels = new Vector<Channel>();
	private Vector<GenericNetworkInterface> interfacesProvidingConnectivity = new Vector<GenericNetworkInterface>();
	private Vector<Path> availablePaths = new Vector<Path>();

	private static RoutingManager routingManager = null;

	public synchronized static RoutingManager getInstance() throws Exception {
		// System.out.println("RoutingManager getInstance ");
		if (routingManager == null) {
			routingManager = new RoutingManager();
			routingManager.start();
		}
		// System.out.println("RoutingManager getInstance "+routingManager);
		return routingManager;
	}

	private RoutingManager() throws Exception {
		this.connectorManager = ConnectorManager.getInstance();
		this.nip = NetworkInterfaceProvider.getInstance();
		// new ServerRoutingManager().start();
	}

	public double getLocalRequiredReliability() {
		return localRequiredReliability;
	}

	public void setLocalRequiredReliability(double newLocalRequiredReliability) {
		this.localRequiredReliability = newLocalRequiredReliability;
	}

	public Vector<Path> getAvailablePaths() {
		return availablePaths;
	}

	public void addAvailablePath(Path path) {
		availablePaths.add(path);
	}

	public void removeAvailablePath(Path path) {
		availablePaths.remove(path);
	}

	public Vector<Channel> getAvailableChannels() {
		return new Vector<Channel>(availableChannels);
	}

	public void removeAvailableChannel(Channel channel) {
		availableChannels.remove(channel);
		// notifica il ConnectorManager della caduta del Channel
		connectorManager.channelLost(channel);
	}

	public void run() {
		System.out.println("RoutingManager run ");
		try {
			while (true) {
				Vector<Channel> newChannels = connectorManager
						.getSuitableChannels();
				System.out.println("RoutingManager newChannels " + newChannels);
				System.out.println("RoutingManager availableChannels "
						+ availableChannels);

				// for each new channel,
				// create a new ClientRoutingManager thread gathering
				// information from the connector
				for (int i = 0; i < newChannels.size(); i++) {
					Channel channel = newChannels.elementAt(i);
					System.out.println("RoutingManager available channel: "
							+ channel);

					boolean isConnectorRole = false;
					GenericNetworkInterface localGNI = channel.getConnector()
							.getLocalNetworkInterface();
					if (localGNI instanceof IEEE80211) {
						isConnectorRole = ((IEEE80211) localGNI)
								.isConnectorRole();
					} else if (localGNI instanceof Bluetooth) {
						isConnectorRole = ((Bluetooth) localGNI)
								.isConnectorRole();
					}
					if (!isConnectorRole) {
						boolean newChannel = true;
						for (int j = 0; j < availableChannels.size()
								&& newChannel == true; j++) {
							Channel oldChannel = availableChannels.elementAt(j);
							if (oldChannel.getChannelLocalIPAddress().equals(
									channel.getChannelLocalIPAddress())) {
								newChannel = false;
							}
						}
						if (newChannel) {
							System.out
									.println("RoutingManager new available channel: "
											+ channel);
							availableChannels.addElement(channel);
							new ClientRoutingManager(channel, this).start();// sono
																			// un
																			// client
																			// con
																			// questo
																			// indirizzo
																			// IP
						}
					}
				}

				// for each new local interface providing connectivity,
				// create a new InterfaceMonitoring thread providing information
				// to clients
				Vector<GenericNetworkInterface> newInterfaces = nip
						.getAvailableInterfaces();
				for (int i = 0; i < newInterfaces.size(); i++) {
					GenericNetworkInterface interf = newInterfaces.elementAt(i);

					boolean isConnectorRole = false;
					if (interf instanceof IEEE80211) {
						isConnectorRole = ((IEEE80211) interf)
								.isConnectorRole();
					} else if (interf instanceof Bluetooth) {
						isConnectorRole = ((Bluetooth) interf)
								.isConnectorRole();
					}
					System.out.println("RoutingManager isConnectorRole "
							+ isConnectorRole + " interf " + interf);

					if (isConnectorRole) {
						boolean newInterf = true;
						for (int j = 0; j < interfacesProvidingConnectivity
								.size()
								&& newInterf == true; j++) {
							GenericNetworkInterface oldInterf = interfacesProvidingConnectivity
									.elementAt(j);
							if (oldInterf.getDescription().equals(
									interf.getDescription())) {
								newInterf = false;
							}
						}
						if (newInterf) {
							System.out.println("RoutingManager newInterf "
									+ newInterf + " interf " + interf);
							interfacesProvidingConnectivity.addElement(interf);
							// if(interf instanceof Bluetooth){
							// //registrazione nell'oggetto Bluetooth per
							// ricevere informazioni
							// //quando si connette qualcuno e si crea quindi
							// un'interfaccia bnepX
							// ((Bluetooth)interf).setConnectionObserver(this);
							// }
							// else
							new ServerRoutingManager(interf, this).start();
						}
						/*
						 * else{
						 * interfacesProvidingConnectivity.removeElement(interf
						 * ); }
						 */
					}
				}

				// TODO AAA di tanto in tanto pulisci le tabelle di routing
				// eliminando tutte le info relative a client non piu'
				// presenti nel file del DHCP server

				Thread.sleep(2000);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Metodo invocato dagli oggetti Bluetooth quando si riceve una nuova
	 * connessione in un'interfaccia impostata per offrire connettivita`
	 * (setConnectionRole)
	 */
	public void newConnection(Bluetooth interf, String networkName,
			String localIP) {
		new ServerRoutingManager(interf, localIP, this).start();
	}
}
